home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 July: Mac OS SDK / Dev.CD Jul 97 SDK1.toast / Development Kits (Disc 1) / Apple Remote Access API / ARA Security API 1.0 / Sample Protocol Module / SampleServerModule.c < prev    next >
Encoding:
Text File  |  1993-11-04  |  19.0 KB  |  651 lines  |  [TEXT/MPS ]

  1. //234567890123456789012345678901234567890123456789012345678901234567890123456789
  2. //===========================================================================
  3. //    File:    SampleServerModule.c
  4. //
  5. //    This is the sample server-side add-on security module for Apple
  6. //    Remote Access.  It works with its corresponding client-side module to
  7. //    prompt the user for an extra password and authenticate the user if the
  8. //    user enters the correct password.  The system administrator can setup the
  9. //    password using the sample server configuration module.
  10. //
  11. //    When invoked this code resource will send a password request to the client
  12. //    code, which will then display a password dialog box.  The client then
  13. //    sends the user password back to this module.  If the password is correct
  14. //    the user is authenticated.
  15. //
  16. //    The module implements a state machine.  The states of the state machine
  17. //    are defined in the TServerCodeStates.  The current state is passed to
  18. //    the completion proc as the CompletionParam, and to the TickleRoutine as
  19. //    the LongParam.
  20. //
  21. //    Copyright © 1992,1993 Apple Computer Inc.
  22. //    All rights reserved
  23. //
  24. //    Author:  Farzad Sarabi
  25. //
  26. //    Modification history:
  27. //
  28. //    5/3/1993    Farzad        Modified for the new SecurityMgr that sets up
  29. //                            A5 world for the modules.
  30. //    12/17/1992    Farzad        Created
  31. //===========================================================================
  32.  
  33.  
  34.  
  35. #include    "SecurityInterface.h"
  36. #include    "SamplePackets.h"
  37.  
  38. // #define SecurityShellVersion 0x0080
  39.  
  40.  
  41. //===========================================================================
  42. //    Macros
  43. //===========================================================================
  44. #define kMaxRetryCount        2        // number of times to retry password
  45.  
  46.  
  47. //===========================================================================
  48. //    Types
  49. //===========================================================================
  50. typedef enum
  51. {
  52.     // 0 is used by ARA for the Tickle routine.  Although we won't be
  53.     // using the tickle routine to handle any states, we'll start with 
  54.     // 1 anyway.
  55.  
  56.     kStateGotOldPassword = 1,        // obtained the old password from U&G
  57.     kStateRequestedPassword,        // sent a request to the client
  58.     kStateGotResponse,                // got response
  59.     kStateSentCancel,                // sent a cancel to the other side
  60.     kStateSentAllow,                // sent allow user
  61.     kStateMiscCompletion            // all other completions we don't care
  62.                                     // about
  63. } TServerCodeStates;
  64.  
  65.  
  66. static struct
  67. {
  68.     TSamplePacket        fout_pkt;    // outgoing packet
  69.     TSamplePacket        fin_pkt;    // incoming packet
  70.  
  71.     char                fold_password[ kSecurityMaxConfigData ];
  72.     int                    ftry_count;
  73.  
  74. } MyData;                            // this is the global data for the
  75.                                     // server side code resource
  76.  
  77.  
  78.  
  79.  
  80. //===========================================================================
  81. //    Functions
  82. //===========================================================================
  83.  
  84. // function prototypes
  85. static long        DoMyStartup( MyReference, LongParam );
  86. static long        DoMyShutdown( MyReference, LongParam );
  87. static long        DoMyBegin( MyReference, LongParam );
  88. static long        DoMyEnd( MyReference, LongParam );
  89. static long        DoMyDataHandler( MyReference, LongParam );
  90. static long        DoMyAbortHandler( MyReference, LongParam );
  91. static long        DoMyTickleHandler( MyReference, LongParam );
  92. pascal void        MyCompletionProc( SecurityReference        MyReference,
  93.                                   int                    ResultCode,
  94.                                   void                    * DataPtr,
  95.                                   int                    DataSize,
  96.                                   long                    CompletionParam );
  97. static int        are_pwds_same( register char        * pwd_1,
  98.                                register char        * pwd_2 );
  99. static void                pstrcpy( register char            * to_str,
  100.                                  register char            * from_str );
  101.  
  102.  
  103.  
  104.  
  105. // this routine is the entry point for this module.  ARA calls this
  106. // routine.
  107. pascal long        MySecurityProcEntry( SecurityActions        Action,
  108.                                      SecurityReference        MyReference,
  109.                                      long                    LongParam )
  110. //===========================================================================
  111. //    Description:    this is the entry point for the ??? security operation.
  112. //                    It is called by AppleTalk Remote Access to have this
  113. //                    security module perform the given operation.  It
  114. //                    dispatches to a variety of routines based on the
  115. //                    requested action.
  116. //
  117. //    Parameters:        Action                the action to be performed
  118. //                    MyReference            this is a unique value representing
  119. //                                        this instance of this code module.
  120. //                    DataPtr                the data for this action
  121. //                    DataSize            the size of data
  122. //
  123. //    Return Value:    long                result code, nonzero indicates an
  124. //                                        error.  Its value is one of the
  125. //                                        SecurityResultCodes.
  126. //
  127. //    Creation Date:
  128. //
  129. //    Modifications:
  130. //
  131. //===========================================================================
  132. {
  133.     switch ( Action )    {
  134.  
  135.       case    kSecurityStartup:
  136.         return DoMyStartup( MyReference, LongParam );
  137.  
  138.  
  139.       case    kSecurityShutdown:
  140.           return DoMyShutdown( MyReference, LongParam );
  141.  
  142.  
  143.       case    kSecurityBegin:
  144.           return DoMyBegin( MyReference, LongParam );
  145.  
  146.  
  147.       case    kSecurityEnd:
  148.           return DoMyEnd( MyReference, LongParam );
  149.  
  150.  
  151.       case    kSecurityDataAvailable:
  152.         return DoMyDataHandler( MyReference, LongParam );
  153.  
  154.  
  155.       case    kSecurityAbort:
  156.         return DoMyAbortHandler( MyReference, LongParam );
  157.  
  158.  
  159.       case    kSecurityTickleAction:
  160.           return DoMyTickleHandler( MyReference, LongParam );
  161.  
  162.     }
  163.  
  164.     return ( kSecurityUnsupportedAction );
  165. }
  166.  
  167.  
  168.  
  169.  
  170. static long        DoMyStartup( MyReference, LongParam )
  171. //===========================================================================
  172. //    Description:    this routine handles the kSecurityStartup action.  You
  173. //                    should allocate any memory and setup the working
  174. //                    environment (e.g. A5 world) here.
  175. //
  176. //    Parameters:        MyReference        My unique reference
  177. //                    LongParam        additional information
  178. //
  179. //    Return Value:    long            result code, nonzero indicates error
  180. //
  181. //    Creation Date:
  182. //
  183. //    Modifications:
  184. //
  185. //===========================================================================
  186. {
  187. #pragma unused(MyReference,LongParam)
  188.  
  189.     // We don't need to do anything for the kSecurityStartup action.
  190.     // Normally we could pre-allocate any needed resources in this
  191.     // routine
  192.  
  193.     return ( kSecurityNoErr );
  194. }
  195.  
  196.  
  197.  
  198. static long        DoMyShutdown( MyReference, LongParam )
  199. //===========================================================================
  200. //    Description:    this routine handles the kSecurityShutdown action.  You
  201. //                    should release any memory allocated by the DoMyStartup
  202. //                    routine.
  203. //
  204. //    Parameters:        MyReference        My unique reference
  205. //                    LongParam        additional information
  206. //
  207. //    Return Value:    long            result code, nonzero indicates error
  208. //
  209. //    Creation Date:
  210. //
  211. //    Modifications:
  212. //
  213. //===========================================================================
  214. {
  215. #pragma unused(MyReference,LongParam)
  216.  
  217.     // We don't need to do anything for the kSecurityShutdown action
  218.     // Normally we would release any allocated resources here.
  219.     // This is the very last action sent to a code resource.
  220.     // After returning from this action, the code resource will
  221.     // be unloaded
  222.  
  223.     return ( kSecurityNoErr );
  224. }
  225.  
  226.  
  227.  
  228. static long        DoMyBegin( MyReference, LongParam )
  229. //===========================================================================
  230. //    Description:    this routine handles the kSecurityBeing action.  This
  231. //                    routine should start the operations the code resource
  232. //                    must do.  For example an authentication code resource
  233. //                    should start the authentication process.
  234. //
  235. //    Parameters:        MyReference        My unique reference
  236. //                    LongParam        additional information
  237. //
  238. //    Return Value:    long            result code, nonzero indicates error
  239. //
  240. //    Creation Date:
  241. //
  242. //    Modifications:
  243. //
  244. //===========================================================================
  245. {
  246. #pragma unused(LongParam)
  247.                                         // get this user's old password
  248.     if ( ARAReadSecurityData( MyReference,
  249.                               & MyData.fold_password,
  250.                               sizeof( MyData.fold_password ),
  251.                               MyCompletionProc,
  252.                               kStateGotOldPassword ) != ARANoErr )
  253.     {
  254.         // Some sort of error occured.  Stop the operation here.
  255.  
  256.         ARACompleteOperation( MyReference );
  257.         return ( kSecurityGenericErr );
  258.     }
  259.  
  260.     return ( kSecurityNoErr );
  261. }
  262.  
  263.  
  264.  
  265. static long        DoMyEnd( MyReference, LongParam )
  266. //===========================================================================
  267. //    Description:    this routine handles the kSecurityEnd action.  The action
  268. //                    is sent to signal the end of the operation the code
  269. //                    resource was created to do.
  270. //
  271. //    Parameters:        MyReference        My unique reference
  272. //                    LongParam        additional information
  273. //
  274. //    Return Value:    long            result code, nonzero indicates error
  275. //
  276. //    Creation Date:
  277. //
  278. //    Modifications:
  279. //
  280. //===========================================================================
  281. {
  282. #pragma unused(MyReference,LongParam)
  283.  
  284.     // The kSecurityEnd action is sent to indicate the end of the operation.
  285.     // We could start the cleanup process here.  ARA services are no longer
  286.     // available.  We could initiate any external (e.g. outstanding calls to
  287.     // Mac's OS) here.  A kSecurityShutdown will be sent later, after which
  288.     // this code resource will be unloaded.
  289.  
  290.     return ( kSecurityNoErr );
  291. }
  292.  
  293.  
  294.  
  295. static long        DoMyDataHandler( MyReference, LongParam )
  296. //===========================================================================
  297. //    Description:    this routine handles the kSecurityDataAvailable action.
  298. //                    The action is sent when data has arrived for the code
  299. //                    resource.
  300. //
  301. //    Parameters:        MyReference        My unique reference
  302. //                    LongParam        additional information
  303. //
  304. //    Return Value:    long            result code, nonzero indicates error
  305. //
  306. //    Creation Date:
  307. //
  308. //    Modifications:
  309. //
  310. //===========================================================================
  311. {
  312. #pragma unused(MyReference,LongParam)
  313.  
  314.     // Currently DataAvailable action is not being used.  It may in the future
  315.     // be put to some use.  One possible use might be for cases when you have
  316.     // a modeless dialog box.  We could send you the events through the use
  317.     // of this action.
  318.  
  319.     // This action might also be used when we get data during
  320.     // the authentication process, and you don't have a read pending.
  321.  
  322.     // Again, it is not used now but may be used in the future!
  323.  
  324.     return ( kSecurityNoErr );
  325. }
  326.  
  327.  
  328.  
  329. static long        DoMyAbortHandler( MyReference, LongParam )
  330. //===========================================================================
  331. //    Description:    this routine handles the kSecurityAbort action.  The
  332. //                    abort action is sent when the code resources operation
  333. //                    needs to be terminated abnormally.
  334. //
  335. //    Parameters:        MyReference        My unique reference
  336. //                    LongParam        additional information
  337. //
  338. //    Return Value:    long            result code, nonzero indicates error
  339. //
  340. //    Creation Date:
  341. //
  342. //    Modifications:
  343. //
  344. //===========================================================================
  345. {
  346. #pragma unused(MyReference,LongParam)
  347.  
  348.     // We are being aborted.  You need to abort what you are doing.  
  349.     // ARAServices will not be available after this (at least the read and
  350.     // writes will not be available, but you shouldn't expect any of the
  351.     // ARA services to be available).  The kSecurityAbort action is sent
  352.     // because of some type of exception.
  353.  
  354.     return ( kSecurityNoErr );
  355. }
  356.  
  357.  
  358.  
  359. static long        DoMyTickleHandler( MyReference, LongParam )
  360. //===========================================================================
  361. //    Description:    this routine handles the kSecurityTickle action.  ARA
  362. //                    sends this action periodically.  The action is also
  363. //                    generated as a result of a call to ARATickleMe routine.
  364. //                    
  365. //
  366. //    Parameters:        MyReference        My unique reference
  367. //                    LongParam        When ARA calls this value will be 0,
  368. //                                    otherwise it is the value passed to
  369. //                                    the ARATickleMe routine.
  370. //
  371. //    Return Value:    long            result code, nonzero indicates error
  372. //
  373. //    Creation Date:
  374. //
  375. //    Modifications:
  376. //
  377. //===========================================================================
  378. {
  379. #pragma unused(MyReference,LongParam)
  380.  
  381.     // This routine is periodically called by ARA.  You can post a tickle
  382.     // action to yourself (e.g. from completion routines) by calling
  383.     // ARATickleMe.  This is usually done to perform actions that need
  384.     // to allocate memory or display dialogs, etc.
  385.     // When ARA is sending the kSecurityTickle action the value of
  386.     // LongParam is 0.  When the action is sent as a result of a call
  387.     // you made to ARATickleMe, the LongParam will be the value you
  388.     // specify in TickleParam for the ARATickleMe call.
  389.     // You can use the LongParam (TickleParam) as a state indicator.
  390.  
  391.     return ( kSecurityNoErr );
  392. }
  393.  
  394.  
  395.  
  396. pascal void        MyCompletionProc( SecurityReference        MyReference,
  397.                                   int                    ResultCode,
  398.                                   void                    * DataPtr,
  399.                                   int                    DataSize,
  400.                                   long                    CompletionParam )
  401. //===========================================================================
  402. //    Description:    this is the completion routine for the ARA calls this
  403. //                    module makes.  It is called when the asynchronous ARA
  404. //                    calls complete.
  405. //                    We use the CompletionParam to represent the current
  406. //                    state.
  407. //
  408. //    Parameters:        MyReference        this code resources unique reference
  409. //                    ResultCode        the result code for the ARA call
  410. //                    DataPtr            pointer to the data we passed to the
  411. //                                    ARA call
  412. //                    DataSize        actual size of data (e.g. for read
  413. //                                    calls this is what was read)
  414. //                    CompletionParam    the current state of our state machine
  415. //
  416. //    Return Value:    none
  417. //
  418. //    Creation Date:    12/17/1992
  419. //
  420. //    Modifications:
  421. //
  422. //===========================================================================
  423. {
  424. #pragma unused(DataPtr,DataSize)
  425.     // This code resource uses one completion routine for all of the
  426.     // asynchronous ARA calls it makes.  It uses the CompletionParam as
  427.     // current state value to implement a state machine.  The value of
  428.     // CompletionParam is the same as the value passed in the CompletionParam
  429.     // of the corresponding asynchronous ARA routine.
  430.  
  431.     if ( ResultCode == ARAAbort )
  432.     {
  433.         // we are being aborted
  434.         return;
  435.     }
  436.  
  437.     switch ( CompletionParam )
  438.     {
  439.  
  440.       case    kStateGotOldPassword:            // have got the old password
  441.  
  442.         // prompt the client code resource to get the user to input the
  443.         // additional password.  At this point, the client code resource
  444.         // should be loaded and waiting for a prompt from this side.
  445.  
  446.         MyData.fout_pkt.fpkt_type = kPktRequestPassword;
  447.         if ( ARAWrite( MyReference,
  448.                        & MyData.fout_pkt,
  449.                        sizeof( MyData.fout_pkt ),
  450.                        MyCompletionProc,
  451.                        kStateRequestedPassword ) != ARANoErr )
  452.         {
  453.             ARACompleteOperation( MyReference );
  454.             return;
  455.         }
  456.  
  457.         // we are going to not allow the user after trying to get the
  458.         // correct password after a few times.
  459.         MyData.ftry_count = 1;
  460.         break;
  461.  
  462.  
  463.       case    kStateRequestedPassword:
  464.           // At this point we have requested the password from the client.
  465.         // We need to post a read to get the client's response.
  466.         if ( ARARead( MyReference,
  467.                       & MyData.fin_pkt,
  468.                       sizeof( MyData.fin_pkt ),
  469.                       MyCompletionProc,
  470.                       kStateGotResponse ) != ARANoErr )
  471.         {
  472.             ARACompleteOperation( MyReference );
  473.             return;
  474.         }
  475.         break;
  476.  
  477.  
  478.       case    kStateGotResponse:                // got response
  479.         if ( ResultCode != ARANoErr )
  480.         {
  481.             ARACompleteOperation( MyReference );
  482.             return;
  483.         }
  484.  
  485.                                             // response is in fin_pkt
  486.         if ( MyData.fin_pkt.fpkt_type == kPktPasswordResponse )
  487.         {    int                allow_user;
  488.             int                next_state;
  489.  
  490.             // The client sent the password the user entered.  Compare it
  491.             // to the password we have stored on the server side.
  492.  
  493.             allow_user = are_pwds_same( MyData.fin_pkt.u.fresponse.fpassword,
  494.                                         MyData.fold_password );
  495.  
  496.             // Regardless of what happens next, we must send the client
  497.             // a response, so we must do a write in all cases.  However
  498.             // we will send different result codes, and the next state
  499.             // in our state machine will be one of several.
  500.             // We will use the local variable next_state to determine
  501.             // what the next state will be that we will transition to.
  502.  
  503.             if ( allow_user )
  504.             {
  505.                 // the password was ok.  Let the client know and
  506.                 // transition to the state that allows the user
  507.  
  508.                 MyData.fout_pkt.fpkt_type = kPktAllow;
  509.                 next_state = kStateSentAllow;
  510.             }
  511.             else
  512.             {
  513.                 // Incorrect password.  Should we retry?
  514.  
  515.                 if ( MyData.ftry_count >= kMaxRetryCount )
  516.                 {    // passed the retry limit.  Don't allow on both
  517.                     // sides.
  518.  
  519.                     MyData.fout_pkt.fpkt_type = kPktCancel;
  520.                     pstrcpy( MyData.fout_pkt.u.freason.fmsg,
  521.                              "\puser was not authenticated" );
  522.                     next_state = kStateSentCancel;
  523.                 }
  524.                 else
  525.                 {    // Request the password from the client again
  526.  
  527.                     ++ MyData.ftry_count;
  528.                     MyData.fout_pkt.fpkt_type = kPktRequestPassword;
  529.                     next_state = kStateRequestedPassword;
  530.                 }
  531.             }
  532.             if ( ARAWrite( MyReference,
  533.                            & MyData.fout_pkt,
  534.                            sizeof( MyData.fout_pkt ),
  535.                            MyCompletionProc,
  536.                            next_state ) != ARANoErr )
  537.             {
  538.                 ARACompleteOperation( MyReference );
  539.                 return;
  540.             }
  541.         }
  542.         else
  543.         {
  544.             // the client sent us something other than the password
  545.             // we requested.  Just don't allow the user
  546.  
  547.             ARADontAllowUser( MyReference,
  548.                               "\pClient canceled authentication",
  549.                               MyCompletionProc,
  550.                               kStateMiscCompletion );
  551.         }
  552.         break;
  553.  
  554.  
  555.       case    kStateSentCancel:
  556.  
  557.         // We have sent the client a cancel.  We need to let ARA know
  558.         // the user is not authenticated, and we are finished.
  559.  
  560.         ARADontAllowUser( MyReference,
  561.                           "\pUser was not authenticated",
  562.                           MyCompletionProc,
  563.                           kStateMiscCompletion );
  564.         break;
  565.  
  566.  
  567.       case    kStateSentAllow:
  568.  
  569.         // We have already let the client know the user is going to be
  570.         // allowed access to ARA.  Now, let ARA know the user was authenticated.
  571.  
  572.           ARAAllowUser( MyReference,
  573.                       MyCompletionProc,
  574.                       kStateMiscCompletion );
  575.         break;
  576.  
  577.  
  578.       case    kStateMiscCompletion:
  579.       default:
  580.           // The kStateMiscCompletion is used for cases that we don't need to
  581.         // do anything for.  
  582.         break;
  583.         
  584.  
  585.     }
  586.  
  587.     return;
  588. }
  589.  
  590.  
  591. static int        are_pwds_same( register char        * pwd_1,
  592.                                register char        * pwd_2 )
  593. //===========================================================================
  594. //    Description:    this routine compares two passwords for equality.  The
  595. //                    strings are pascal strings.
  596. //
  597. //    Parameters:        pwd_1        one of the two pascal string passwords
  598. //                    pwd_2        the other pascal string password (amazing!)
  599. //
  600. //    Return Value:    int            nonzero if the two passwords are equal
  601. //                                zero otherwise
  602. //
  603. //    Creation Date:    12/17/1992
  604. //
  605. //    Modifications:
  606. //
  607. //===========================================================================
  608. {    register int            ri;
  609.  
  610.     // Basically compares two pascal strings.
  611.     // Note:  it is case sensitive
  612.  
  613.     for ( ri = pwd_1[ 0 ] ; ri >= 0; -- ri )
  614.     {    // a little faster than doing ri <= pwd_1[ 0 ]
  615.  
  616.         // first time around compares the length bytes
  617.  
  618.         if ( * ( pwd_1 ++ ) != * ( pwd_2 ++ ))
  619.         {
  620.             return ( false );
  621.         }
  622.     }
  623.  
  624.     return ( true );                    // they match
  625. }
  626.  
  627.  
  628. static void                pstrcpy( register char            * to_str,
  629.                                  register char            * from_str )
  630. //===========================================================================
  631. //    Description:    copies a pascal string from from_str to to_str.
  632. //
  633. //    Parameters:        to_str        where the string is copied to
  634. //                    from_str    where the string is copied from
  635. //
  636. //    Return Value:    none
  637. //
  638. //    Creation Date:    12/17/1992
  639. //
  640. //    Modifications:
  641. //
  642. //===========================================================================
  643. {    register int        ri;
  644.  
  645.     for ( ri = from_str[ 0 ]; ri >= 0; -- ri )
  646.     {
  647.         * ( to_str ++ ) = * ( from_str ++ );
  648.     }
  649.  
  650.     return;
  651. }